home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / storage / smgr / pgjb.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  24.0 KB  |  930 lines

  1. /*
  2.  *  jbconn.c -- Manage Sony jukebox connections for sj storage manager.
  3.  *
  4.  *    This file is only included in the compiled version of Postgres
  5.  *    if SONY_JUKEBOX is defined, which, in turn, should only be true
  6.  *    if you are using the Sony WORM optical disk jukebox at Berkeley.
  7.  */
  8.  
  9. #include "tmp/c.h"
  10. #include "tmp/postgres.h"
  11.  
  12. #ifdef SONY_JUKEBOX
  13.  
  14. RcsId("$Header: /private/postgres/src/storage/smgr/RCS/pgjb.c,v 1.15 1992/06/30 22:44:28 mao Exp $");
  15.  
  16. #include <math.h>
  17. #include <sys/file.h>
  18. #include "machine.h"
  19.  
  20. #include "storage/block.h"
  21. #include "storage/ipc.h"
  22. #include "storage/ipci.h"
  23. #include "storage/smgr.h"
  24. #include "storage/shmem.h"
  25. #include "storage/spin.h"
  26. #include "storage/sj.h"
  27.  
  28. #include "utils/hsearch.h"
  29. #include "utils/log.h"
  30. #include "utils/rel.h"
  31.  
  32. #include "storage/jbstruct.h"
  33. #include "storage/jblib.h"
  34.  
  35. #include "access/htup.h"
  36. #include "access/relscan.h"
  37. #include "access/heapam.h"
  38.  
  39. #include "catalog/pg_platter.h"
  40. #include "catalog/pg_proc.h"
  41.  
  42. /*
  43.  *  JBHashEntry -- In shared memory, we maintain a hash table with the number
  44.  *  of blocks allocated to a platter, keyed by the platter's OID in the
  45.  *  pg_platter catalog.
  46.  */
  47.  
  48. typedef struct JBHashEntry {
  49.     ObjectId    jbhe_oid;
  50.     BlockNumber    jbhe_nblocks;
  51. } JBHashEntry;
  52.  
  53. /*
  54.  *  In order to avoid dying if hermes is down, we postpone establishing
  55.  *  a connection to the jukebox until we absolutely have to do so.  This
  56.  *  macro is invoked at the top of the public interface routines that
  57.  *  actually do any jukebox operations.
  58.  */
  59.  
  60. #define VRFY_CONNECT()    if (!JBConnected) _pgjb_connect()
  61.  
  62. #define JBCACHESIZE    100            /* one entry per platter */
  63. #define PGJBFORMAT    "POSTGRES_FMT"        /* format string for jb_open */
  64. #define JBRETRY        3            /* # times to retry writes */
  65.  
  66. /*
  67.  *  JBPlatDesc -- Description of an open platter in the jukebox.
  68.  *
  69.  *    We keep these in private memory, in a hash table.  Every time we
  70.  *    open a new platter, we create a new platter descriptor and add it
  71.  *    to the table.  Once we open a platter, we keep it open until the
  72.  *    backend terminates.  The autochanger at the other end of the
  73.  *    jukebox connection will shuffle platters in and out of drives for
  74.  *    us.
  75.  */
  76.  
  77. typedef struct JBPlatDesc {
  78.     ObjectId    jbpd_plid;
  79.     JBPLATTER    *jbpd_platter;
  80. } JBPlatDesc;
  81.  
  82. #define    JBPH_STARTSIZE    10
  83.  
  84. /* globals defined here */
  85. static int        *JBNEntries;
  86. static HTAB        *JBHash;
  87. static bool        JBConnected = false;
  88. static HTAB        *JBPlatHash;
  89.  
  90. SPINLOCK        JBSpinLock;
  91.  
  92. /* routines declared here */
  93. extern int        pgjb_init();
  94. extern BlockNumber    pgjb_offset();
  95. extern int        pgjb_wrtextent();
  96. extern int        pgjb_rdextent();
  97. extern int        JBShmemSize();
  98. static void        _pgjb_connect();
  99. static JBPlatDesc    *_pgjb_getplatdesc();
  100. static JBHashEntry    *_pgjb_hashget();
  101. static BlockNumber    _pgjb_findoffset();
  102. static int        _pgjb_retry();
  103. static int        _pgjb_mdblockrd();
  104. static void        _pgjb_mdblockwrt();
  105.  
  106. /* routines declared elsewhere */
  107. extern HTAB        *ShmemInitHash();
  108. extern int        *ShmemInitStruct();
  109. extern BlockNumber    sjmaxseg();
  110. extern int        mylog2();
  111.  
  112. /*
  113.  *  pgjb_init() -- Initialize data structures used to communicate with the
  114.  *           Sony jukebox under POSTGRES.
  115.  *
  116.  *    This routine is called from sjinit().  We use some structures in
  117.  *    shared memory, and some in private memory, to do this.  Shared
  118.  *    memory stores a hash table of the highest block number allocated
  119.  *    on a given platter so far; we use this to allocate new extents
  120.  *    to platters.  In private memory, we keep a record of what connections
  121.  *    we currently have open to the jukebox.
  122.  */
  123.  
  124. int
  125. pgjb_init()
  126. {
  127.     bool found;
  128.     HASHCTL info;
  129.  
  130.     /* exclusive access required */
  131.     SpinAcquire(JBSpinLock);
  132.  
  133.     /*
  134.      *  Get the shared memory block (actually, the shared memory integer)
  135.      *  that tells us how full the hash table is.
  136.      */
  137.  
  138.     JBNEntries = ShmemInitStruct("Jukebox connection metadata",
  139.                  sizeof(*JBNEntries), &found);
  140.  
  141.     if (JBNEntries == (int *) NULL) {
  142.     SpinRelease(JBSpinLock);
  143.     return (SM_FAIL);
  144.     }
  145.  
  146.     /* init it if we need to */
  147.     if (!found)
  148.     *JBNEntries = 0;
  149.  
  150.     /*
  151.      *  Get the shared memory hash table that maps platter OIDs to next free
  152.      *  block.  Hash table entries are SJHashEntry structures.
  153.      */
  154.  
  155.     info.keysize = sizeof(ObjectId);
  156.     info.datasize = sizeof(BlockNumber);
  157.     info.hash = tag_hash;
  158.  
  159.     JBHash = ShmemInitHash("Jukebox platter map", JBCACHESIZE, JBCACHESIZE,
  160.                &info, (HASH_ELEM|HASH_FUNCTION));
  161.  
  162.     if (JBHash == (HTAB *) NULL) {
  163.     SpinRelease(JBSpinLock);
  164.     return (SM_FAIL);
  165.     }
  166.  
  167.     /* done with shared initialization */
  168.     SpinRelease(JBSpinLock);
  169.  
  170.     /*
  171.      *  Now initialize data structures in private memory that we use for
  172.      *  jukebox connections.  We don't establish a connection to the
  173.      *  jukebox until we actually need to use it.
  174.      */
  175.  
  176.     bzero(&info, sizeof(info));
  177.     info.keysize = sizeof(ObjectId);
  178.     info.datasize = sizeof(JBPLATTER *);
  179.     info.hash = tag_hash;
  180.  
  181.     JBPlatHash = hash_create(JBPH_STARTSIZE, &info, (HASH_ELEM|HASH_FUNCTION));
  182.  
  183.     if (JBPlatHash == (HTAB *) NULL)
  184.     return (SM_FAIL);
  185.  
  186.     return (SM_SUCCESS);
  187. }
  188.  
  189. /*
  190.  *  _pgjb_connect() -- Establish a connection to the jukebox.
  191.  *
  192.  *    We postpone doing this for as long as possible.  Whenever we try
  193.  *    to operate on a platter, we check to see if we've already opened
  194.  *    a connection.  If not, we call this routine.
  195.  *
  196.  *    On success, we just return.  On failure, we elog(WARN, ...), which
  197.  *    aborts the transaction.  Should we elog(FATAL, ...) instead?
  198.  */
  199.  
  200. static void
  201. _pgjb_connect()
  202. {
  203.     if (JB_INIT() < 0)
  204.     elog(WARN, "cannot connect to jukebox server.");
  205.  
  206.     JBConnected = true;
  207. }
  208.  
  209. /*
  210.  *  pgjb_offset() -- Find offset of first free extent on platter.
  211.  *
  212.  *    If we're lucky, we'll have this in the shared memory hash table.
  213.  *    If we're not lucky, we have to visit the cache and the platter
  214.  *    in order to figure out where the first free block is.
  215.  *
  216.  *    On entry into this routine, we hold no locks.  We acquire the
  217.  *    jukebox lock in order to query the hash table.  For now, we wind
  218.  *    up holding this (exclusive) lock during IO, if we wind up needing
  219.  *    to compute the first free block number.  This is VERY slow and
  220.  *    needs to be fixed.
  221.  */
  222.  
  223. BlockNumber
  224. pgjb_offset(plname, plid, extentsz)
  225.     char *plname;
  226.     ObjectId plid;
  227.     int extentsz;
  228. {
  229.     JBHashEntry *entry;
  230.     BlockNumber offset;
  231.     bool found;
  232.  
  233.     /* be sure we have a connection */
  234.     VRFY_CONNECT();
  235.  
  236.     SpinAcquire(JBSpinLock);
  237.  
  238.     /* get the entry for plid from the shared hash table */
  239.     entry = _pgjb_hashget(plid);
  240.  
  241.     /*
  242.      *  If we haven't yet computed the first free block offset for this
  243.      *  platter, we need to do that.
  244.      */
  245.  
  246.     if (entry->jbhe_nblocks == InvalidBlockNumber) {
  247.  
  248.     /*
  249.      *  Pass the actual size of an extent to findoffset, since
  250.      *  it uses that to compute probable locations for extents
  251.      *  to start.  Since the user may have passed zero in to this
  252.      *  routine, we don't want to propogate the user's value.
  253.      */
  254.  
  255.     offset = _pgjb_findoffset(plname, plid, SJEXTENTSZ);
  256.  
  257.     if (offset == InvalidBlockNumber) {
  258.         SpinRelease(JBSpinLock);
  259.         elog(FATAL, "pgjb_offset:  cannot find first free block <%s,%d>",
  260.             plname, plid);
  261.     }
  262.     } else {
  263.     offset = entry->jbhe_nblocks;
  264.     }
  265.  
  266.     /* update shared memory state to reflect the allocation */
  267.     entry->jbhe_nblocks = offset + extentsz;
  268.  
  269.     SpinRelease(JBSpinLock);
  270.  
  271.     return (offset);
  272. }
  273.  
  274. /*
  275.  *  pgjb_freespc() -- Be sure there's sufficient space on the platter for
  276.  *              an allocation.
  277.  *
  278.  *    If a new relation is being allocated to this platter, our policy
  279.  *    is that the platter must be no more than 90% full.  This permits
  280.  *    existing relations on the platter to grow, which provides coarse
  281.  *    clustering.  If this is not an allocation for a new relation,
  282.  *    then all that we require is that there be one extent free on the
  283.  *    platter.
  284.  */
  285.  
  286. bool
  287. pgjb_freespc(plname, plid, alloctype)
  288.     char *plname;
  289.     ObjectId plid;
  290.     int alloctype;
  291. {
  292.     BlockNumber hiblock;
  293.     BlockNumber maxblock;
  294.  
  295.     /* to find high block number, allocate a size zero extent... */
  296.     hiblock = pgjb_offset(plname, plid, 0);
  297.  
  298.     if (alloctype == SJNEWRELN)
  299.     maxblock = (JB_MAX_BLOCK / 10) * 9;
  300.     else
  301.     maxblock = JB_MAX_BLOCK - SJEXTENTSZ;
  302.  
  303.     return ((bool) (hiblock <= maxblock));
  304. }
  305.  
  306. /*
  307.  *  _pgjb_hashget() -- Find a shared memory hash table record by platter id.
  308.  *
  309.  *    If the requested platter id is in the shared cache, we return a
  310.  *    pointer to its hash table entry.  If it's not there yet, we enter
  311.  *    it and return a pointer to the new entry.  We're careful not to
  312.  *    exceed the capacity of the hash table.
  313.  *
  314.  *    On entry and exit, we hold the jukebox spin lock.
  315.  */
  316.  
  317. static JBHashEntry *
  318. _pgjb_hashget(plid)
  319.     ObjectId plid;
  320. {
  321.     JBHashEntry *entry;
  322.     bool found;
  323.  
  324.     entry = (JBHashEntry *) hash_search(JBHash, (char *) &plid,
  325.                     HASH_FIND, &found);
  326.  
  327.     if (entry == (JBHashEntry *) NULL) {
  328.     SpinRelease(JBSpinLock);
  329.     elog(FATAL, "_pgjb_hashget: shared hash table corrupt on FIND");
  330.     }
  331.  
  332.     if (found)
  333.     return (entry);
  334.  
  335.     if (*JBNEntries == JBCACHESIZE) {
  336.     SpinRelease(JBSpinLock);
  337.     elog(WARN, "_pgjb_hashget: cannot enter %d: shared hash table full",
  338.            plid);
  339.     }
  340.  
  341.     /* entering a new plid */
  342.     (*JBNEntries)++;
  343.  
  344.     entry = (JBHashEntry *) hash_search(JBHash, (char *) &plid,
  345.                     HASH_ENTER, &found);
  346.  
  347.     if (entry == (JBHashEntry *) NULL) {
  348.     SpinRelease(JBSpinLock);
  349.     elog(FATAL, "_pgjb_hashget: shared hash table corrupt on ENTER");
  350.     }
  351.  
  352.     /* have not yet computed first free block for this entry */
  353.     entry->jbhe_nblocks = InvalidBlockNumber;
  354.  
  355.     return (entry);
  356. }
  357.  
  358. /*
  359.  *  _pgjb_findoffset() -- Find offset of first free extent on this platter.
  360.  *
  361.  *    This is an extremely expensive call; we work hard to make it as
  362.  *    seldom as possible.
  363.  *
  364.  *    The basic idea is to find the last occupied segment, and to add
  365.  *    extentsz blocks to that to get the offset of the first free block.
  366.  *    In order to find the last occupied segment, we must consult both
  367.  *    the shared cache on magnetic disk, and the platter itself.  We first
  368.  *    find the highest occupied segment we know about in the magnetic disk
  369.  *    cache.  We begin scanning for an unoccupied segment on the platter
  370.  *    from there.
  371.  *
  372.  *    We hold the jukebox spin lock throughout, and also wind up acquiring
  373.  *    the sony jukebox cache lock during our scan of the magnetic disk
  374.  *    cache.  This is a lot of locks held for a long time.  We ought to do
  375.  *    something smarter.
  376.  */
  377.  
  378. static BlockNumber
  379. _pgjb_findoffset(plname, plid, extentsz)
  380.     char *plname;
  381.     ObjectId plid;
  382.     int extentsz;
  383. {
  384.     BlockNumber last;
  385.     BlockNumber platfirst;
  386.     BlockNumber extentno;
  387.     long blkno;
  388.     JBPlatDesc *jbp;
  389.     Relation plat;
  390.     TupleDescriptor platdesc;
  391.     HeapScanDesc platscan;
  392.     HeapTuple plattup;
  393.     Datum d;
  394.     bool n;
  395.     ScanKeyEntryData skey;
  396.  
  397.     if ((jbp = _pgjb_getplatdesc(plname, plid)) == (JBPlatDesc *) NULL)
  398.     return (InvalidBlockNumber);
  399.  
  400.     /*
  401.      *  check the mag disk cache for highest-numbered segment, and allocate
  402.      *  the segment following it.
  403.      */
  404.     last = sjmaxseg(plid);
  405.     if (last != InvalidBlockNumber)
  406.     last += extentsz;
  407.     else
  408.     last = 0;
  409.  
  410.     /* see if there's a starting location stored in pg_platter */
  411.     ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber,
  412.                ObjectIdEqualRegProcedure,
  413.                ObjectIdGetDatum(plid));
  414.  
  415.     plat = heap_openr(Name_pg_platter);
  416.     platdesc = RelationGetTupleDescriptor(plat);
  417.     platscan = heap_beginscan(plat, false, NowTimeQual, 1, &skey);
  418.     plattup = heap_getnext(platscan, false, (Buffer *) NULL);
  419.     if (!HeapTupleIsValid(plattup))
  420.     elog(WARN, "missing pg_platter tuple oid %ld", plid);
  421.  
  422.     d = (Datum) heap_getattr(plattup, InvalidBuffer, Anum_pg_platter_plstart,
  423.                  platdesc, &n);
  424.  
  425.     /* null means zero to us */
  426.     if (n)
  427.     platfirst = 0;
  428.     else
  429.     platfirst = DatumGetInt32(d);
  430.  
  431.     if (platfirst > last)
  432.     last = platfirst;
  433.  
  434.     /*
  435.      *  Starting at the first extent after the last known allocated extent,
  436.      *  search for a free extent on the platter.  We must start at an integral
  437.      *  multiple of extentsz blocks on the platter.
  438.      */
  439.  
  440.     extentno = last / extentsz;
  441.  
  442.     if (extentno * extentsz != last) {
  443.     extentno++;
  444.     last = extentno * extentsz;
  445.     }
  446.  
  447.     do {
  448.     /* see if block 'last' is written */
  449.     blkno = jb_scanw(jbp->jbpd_platter, last, 1);
  450.  
  451.     /* if so, skip to next extent */
  452.     if (blkno >= 0)
  453.         last += extentsz;
  454.     } while (blkno >= 0);
  455.  
  456.     /* XXX should use symbolic constant */
  457.     if (blkno != -2L) {
  458.     elog(NOTICE, "_pgjb_findoffset: scanw failed on <%s,%d>: %ld",
  459.              plname, plid, blkno);
  460.     }
  461.  
  462.     return (last);
  463. }
  464.  
  465. /*
  466.  *  _pgjb_getplatdesc() -- Get platter descriptor from private hash table.
  467.  *
  468.  *    This routine enters the platter by plid if necessary, and returns
  469.  *    a pointer to a JBPlatDesc structure containing an open JBPLATTER
  470.  *    record.
  471.  */
  472.  
  473. static JBPlatDesc *
  474. _pgjb_getplatdesc(plname, plid)
  475.     char *plname;
  476.     ObjectId plid;
  477. {
  478.     JBPlatDesc *jbp;
  479.     bool found;
  480.  
  481.     jbp = (JBPlatDesc *) hash_search(JBPlatHash, (char *) &plid,
  482.                      HASH_ENTER, &found);
  483.  
  484.     if (jbp == (JBPlatDesc *) NULL) {
  485.     elog(NOTICE, "_pgjb_getplatdesc: private hash table corrupt");
  486.     return ((JBPlatDesc *) NULL);
  487.     }
  488.  
  489.     if (!found) {
  490.     jbp->jbpd_platter = jb_open(plname, PGJBFORMAT, JB_RDWR);
  491.     if (jbp->jbpd_platter == (JBPLATTER *) NULL) {
  492.         elog(NOTICE, "_pgjb_getplatdesc: cannot open <%s,%d>",
  493.              plname, plid);
  494.         return ((JBPlatDesc *) NULL);
  495.     }
  496.     }
  497.  
  498.     return (jbp);
  499. }
  500.  
  501. /*
  502.  *  pgjb_wrtextent() -- Write an extent to the jukebox.
  503.  *
  504.  *    This routine takes a pointer to the SJCacheBuf buffer from sj.c,
  505.  *    and a pointer to the SJ cache item that describes it.  Item includes
  506.  *    a description of the write that needs to be done.  As a side effect,
  507.  *    this routine modifies flags in item to reflect the write.
  508.  */
  509.  
  510. int
  511. pgjb_wrtextent(item, relblocks, buf)
  512.     SJCacheItem *item;
  513.     int relblocks;
  514.     char *buf;
  515. {
  516.     SJGroupDesc *group;
  517.     JBPlatDesc *jbp;
  518.     int i;
  519.     int startoff, startblk;
  520.     int nblocks;
  521.     int status;
  522.     char *plname;
  523.  
  524.     /* be sure we have a connection */
  525.     VRFY_CONNECT();
  526.  
  527.     plname = (char *) palloc(sizeof(NameData) + 1);
  528.     strncpy(plname, &(item->sjc_plname.data[0]), sizeof(NameData));
  529.     plname[sizeof(NameData)] = '\0';
  530.  
  531.     SpinAcquire(JBSpinLock);
  532.     jbp = _pgjb_getplatdesc(plname, item->sjc_plid);
  533.     SpinRelease(JBSpinLock);
  534.  
  535.     pfree(plname);
  536.  
  537.     if (jbp == (JBPlatDesc *) NULL) {
  538.     elog(NOTICE, "pgjb_wrtextent: cannot get platter <%s,%d>",
  539.              plname, item->sjc_plid);
  540.     return (SM_FAIL);
  541.     }
  542.  
  543.     group = (SJGroupDesc *) buf;
  544.  
  545.     if (!(item->sjc_gflags & SJC_ONPLATTER)) {
  546.     item->sjc_gflags |= SJC_ONPLATTER;
  547.     nblocks = 1;
  548.     startoff = 0;
  549.     startblk = 0;
  550.     } else {
  551.     nblocks = 0;
  552.     }
  553.  
  554.     /*
  555.      *  Block zero in the buffer is the group descriptor; this block is of
  556.      *  size JBBLOCKSZ.  There are SJGRPSIZE blocks of size BLCKSZ that
  557.      *  follow.  We do some hocus-pocus for each group to locate the first
  558.      *  and last blocks of size JBBLOCKSZ in the buffer at which we have
  559.      *  data that needs to be written.
  560.      *
  561.      *    We batch these writes up, and submit a single request for as many
  562.      *  adjacent blocks as we can.  We have to be careful to put the last
  563.      *  block in the relation on magnetic disk, not on the optical platter.
  564.      *  That complicates the loop below substantially.
  565.      */
  566.  
  567.     for (i = 0; i < SJGRPSIZE; i++) {
  568.     if (MUST_FLUSH(item->sjc_flags[i])
  569.         && ((item->sjc_tag.sjct_base + i + 1) < relblocks)) {
  570.  
  571.         if (nblocks == 0) {
  572.         startblk = (i * (BLCKSZ / JBBLOCKSZ)) + 1;
  573.         startoff = (i * BLCKSZ) + JBBLOCKSZ;
  574.         }
  575.  
  576.         item->sjc_flags[i] |= SJC_ONPLATTER;
  577.         nblocks += (BLCKSZ / JBBLOCKSZ);
  578.  
  579.     } else {
  580.  
  581.         /*
  582.          *  If this is the last block in the relation, then we need
  583.          *  to put it on magnetic disk.
  584.          */
  585.  
  586.         if (MUST_FLUSH(item->sjc_flags[i])
  587.         && ((item->sjc_tag.sjct_base + i + 1) == relblocks)) {
  588.  
  589.         _pgjb_mdblockwrt(item, relblocks, buf);
  590.         }
  591.  
  592.         /*
  593.          *  If there are bytes waiting to go out to the platter,
  594.          *  write them.
  595.          */
  596.  
  597.         if (nblocks > 0) {
  598.         /* got some blocks -- write them */
  599.         status = jb_write(jbp->jbpd_platter,
  600.                   &(buf[startoff]),
  601.                   group->sjgd_jboffset + startblk,
  602.                   nblocks);
  603.  
  604.         if (status < 0) {
  605.             status = _pgjb_retry(jbp->jbpd_platter,
  606.                      &(buf[startoff]),
  607.                      group->sjgd_jboffset + startblk,
  608.                      nblocks);
  609.             if (status < 0) {
  610.             elog(NOTICE, "_pgjb_wrtextent: write failed");
  611.             return (SM_FAIL);
  612.             }
  613.         }
  614.  
  615.         nblocks = 0;
  616.         }
  617.     }
  618.     }
  619.  
  620.     /* handle any blocks not written above */
  621.     if (nblocks > 0) {
  622.     /* got some blocks -- write them */
  623.     status = jb_write(jbp->jbpd_platter,
  624.               &(buf[startoff]),
  625.               group->sjgd_jboffset + startblk,
  626.               nblocks);
  627.  
  628.     if (status < 0) {
  629.         /* silent retry */
  630.         status = _pgjb_retry(jbp->jbpd_platter,
  631.                  &(buf[startoff]),
  632.                  group->sjgd_jboffset + startblk,
  633.                  nblocks);
  634.  
  635.         if (status < 0) {
  636.         elog(NOTICE, "_pgjb_wrtextent: write failed");
  637.         return (SM_FAIL);
  638.         }
  639.     }
  640.     }
  641.  
  642.     return (SM_SUCCESS);
  643. }
  644.  
  645. static int
  646. _pgjb_retry(jbplatter, buf, ploffset, nblocks)
  647.     JBPLATTER *jbplatter;
  648.     char *buf;
  649.     int ploffset;
  650.     int nblocks;
  651. {
  652.     int i, j;
  653.     int status;
  654.     int off;
  655.     char *vrfybuf;
  656.  
  657.     elog(NOTICE, "write at platter offset %d failed, retrying...", ploffset);
  658.     vrfybuf = (char *) palloc(JBBLOCKSZ);
  659.  
  660.     for (i = 0; i < nblocks; i++) {
  661.     off = i * JBBLOCKSZ;
  662.  
  663.     for (j = 0; j < JBRETRY; j++) {
  664.  
  665.         /* first, try to read this block */
  666.         status = jb_read(jbplatter, vrfybuf, ploffset + i, 1);
  667.  
  668.         if (status < 0) {
  669.         /* if read fails, try to write the block */
  670.         status = jb_write(jbplatter, &buf[off], ploffset + i, 1);
  671.  
  672.         /* if write succeeded, get set to verify */
  673.         if (status == 0) {
  674.             status = jb_read(jbplatter, vrfybuf, ploffset + i, 1);
  675.  
  676.             /* 'break' is for the for (j = 0; ...) loop */
  677.             if (status == 0)
  678.             break;
  679.         }
  680.         }
  681.     }
  682.  
  683.     /* on success, verify */
  684.     if (status == 0) {
  685.         if (bcmp(&buf[off], vrfybuf, JBBLOCKSZ) != 0) {
  686.         pfree (vrfybuf);
  687.         return (-1);
  688.         }
  689.     }
  690.     }
  691.  
  692.     /* by here, we managed to squeeze all the blocks out after all */
  693.     pfree(vrfybuf);
  694.     elog(NOTICE, "retry succeeded");
  695.  
  696.     return (0);
  697. }
  698.  
  699. /*
  700.  *  pgjb_rdextent() -- Read an extent off of a platter.
  701.  *
  702.  *    This routine takes an SJCacheItem pointer and a pointer to the
  703.  *    char buffer from sj.c, just like pgjb_wrtextent().  We read in
  704.  *    the desired extent, setting flags in the cache item structure
  705.  *    as appropriate.
  706.  *
  707.  *    Due to a design problem in the Sony jukebox driver and library
  708.  *    code, if the entire extent has not been written to disk (which
  709.  *    may happen, for example, when we kick out the highest extent of
  710.  *    any given relation), our request to read the extent in a single
  711.  *    call will fail.  When it fails, it won't return any data.  In
  712.  *    this case, we have to issue lots of single-block reads in order
  713.  *    to figure out which blocks in the extent are actually present,
  714.  *    and which are not.
  715.  */
  716.  
  717. int
  718. pgjb_rdextent(item, buf)
  719.     SJCacheItem *item;
  720.     char *buf;
  721. {
  722.     JBPlatDesc *jbp;
  723.     SJGroupDesc *group;
  724.     Relation reln;
  725.     char *plname;
  726.     int i;
  727.     int status;
  728.     int nblocks;
  729.     int jboffset;
  730.  
  731.     /* be sure we have a connection */
  732.     VRFY_CONNECT();
  733.  
  734.     plname = (char *) palloc(sizeof(NameData) + 1);
  735.     strncpy(plname, &(item->sjc_plname.data[0]), sizeof(NameData));
  736.     plname[sizeof(NameData)] = '\0';
  737.  
  738.     SpinAcquire(JBSpinLock);
  739.     jbp = _pgjb_getplatdesc(plname, item->sjc_plid);
  740.     SpinRelease(JBSpinLock);
  741.  
  742.     pfree(plname);
  743.  
  744.     if (jbp == (JBPlatDesc *) NULL) {
  745.     elog(NOTICE, "pgjb_rdextent: cannot get platter <%s,%d>",
  746.              plname, item->sjc_plid);
  747.     return (SM_FAIL);
  748.     }
  749.  
  750.     status = jb_read(jbp->jbpd_platter, buf, item->sjc_jboffset, SJEXTENTSZ);
  751.  
  752.     /*
  753.      *  If we failed to read the whole extent, then we don't know what's
  754.      *  out there, and we need to read one block at a time.  This is tedious.
  755.      */
  756.  
  757.     if (status < 0) {
  758.  
  759.     /* first read the group descriptor */
  760.     status = jb_read(jbp->jbpd_platter, &buf[0], item->sjc_jboffset, 1);
  761.     if (status < 0) {
  762.         elog(NOTICE, "pgjb_rdextent: group descriptor missing <%d>@%d",
  763.              item->sjc_plid, item->sjc_jboffset);
  764.         return (SM_FAIL);
  765.     }
  766.  
  767.     /* group descriptor block is out there already */
  768.     item->sjc_gflags |= SJC_ONPLATTER;
  769.  
  770.     /*
  771.      *  For each block in the extent, try to read the data off the
  772.      *  platter.  If the read fails, we assume that the block is
  773.      *  missing.
  774.      */
  775.  
  776.     for (i = 0; i < SJGRPSIZE; i++) {
  777.  
  778.         jboffset = item->sjc_jboffset + (i * (BLCKSZ / JBBLOCKSZ)) + 1;
  779.         status = jb_read(jbp->jbpd_platter,
  780.                  &(buf[(i * BLCKSZ) + JBBLOCKSZ]),
  781.                  jboffset, BLCKSZ / JBBLOCKSZ);
  782.  
  783.         if (status < 0) {
  784.         item->sjc_flags[i] = SJC_MISSING;
  785.         } else {
  786.         item->sjc_flags[i] = SJC_ONPLATTER;
  787.         }
  788.     }
  789.  
  790.     /*
  791.      *  If the entire extent wasn't on the platter, it's possible that
  792.      *  this is the last extent in the relation, and the last block
  793.      *  lives on magnetic disk.  Figure out if this is the case, and
  794.      *  if so, instantiate the block.
  795.      */
  796.  
  797.     reln = (Relation) RelationIdGetRelation(item->sjc_tag.sjct_relid);
  798.  
  799.     if (reln == (Relation) NULL)
  800.         elog(WARN, "_pgjb_mdblockrd: can't find reldesc for %d",
  801.                item->sjc_tag.sjct_relid);
  802.  
  803.     nblocks = sjnblocks(reln);
  804.     if (nblocks <= (item->sjc_tag.sjct_base + SJGRPSIZE + 1)) {
  805.         if (_pgjb_mdblockrd(reln, item, buf, nblocks - 1) == SM_FAIL)
  806.         return (SM_FAIL);
  807.     }
  808.  
  809.     } else {
  810.     /* the entire extent is on the platter */
  811.     item->sjc_gflags |= SJC_ONPLATTER;
  812.     for (i = 0; i < SJGRPSIZE; i++)
  813.         item->sjc_flags[i] = SJC_ONPLATTER;
  814.     }
  815.  
  816.     /* record OID of group on platter in item */
  817.     group = (SJGroupDesc *) buf;
  818.     item->sjc_oid = group->sjgd_groupoid;
  819.  
  820.     /* sanity check */
  821.     if (group->sjgd_magic != SJGDMAGIC || group->sjgd_version != SJGDVERSION)
  822.     return (SM_FAIL);
  823.  
  824.     return (SM_SUCCESS);
  825. }
  826.  
  827. /*
  828.  *  _pgjb_mdblockwrt -- Write a particular block to the magnetic disk.
  829.  *
  830.  *    The highest-numbered block for any relation is always stored on
  831.  *    magnetic disk.  This routine pushes it out.  It either returns
  832.  *    successfully or exits.  XXX -- right now, dies holding locks.
  833.  */
  834.  
  835. static void
  836. _pgjb_mdblockwrt(item, relblocks, buf)
  837.     SJCacheItem *item;
  838.     int relblocks;
  839.     char *buf;
  840. {
  841.     SJGroupDesc *group;
  842.     File vfd;
  843.     int which;
  844.     int offset;
  845.     char path[SJPATHLEN];
  846.  
  847.     which = (relblocks - 1) % SJGRPSIZE;
  848.     offset = (which * BLCKSZ) + JBBLOCKSZ;
  849.     group = (SJGroupDesc *) buf;
  850.     sprintf(&(path[0]), "../%s/%s",
  851.         &(group->sjgd_dbname.data[0]), &(group->sjgd_relname.data[0]));
  852.  
  853.     if ((vfd = PathNameOpenFile(&(path[0]), O_RDWR, 0600)) < 0)
  854.     elog(FATAL, "_pgjb_mdblockwrt: can't open %s", &(path[0]));
  855.  
  856.     if (FileSeek(vfd, 0L, L_SET) != 0L)
  857.     elog(FATAL, "_pgjb_mdblockwrt: can't seek to 0 on %s", &(path[0]));
  858.  
  859.     if (FileWrite(vfd, &buf[offset], BLCKSZ) < 0)
  860.     elog(FATAL, "_pgjb_mdblockwrt: write failed on %s", &(path[0]));
  861.  
  862.     (void) FileClose(vfd);
  863. }
  864. /*
  865.  *  _pgjb_mdblockrd -- Read a particular block off of magnetic disk.
  866.  *
  867.  *    The highest-numbered block for any relation is always stored on
  868.  *    magnetic disk.  This routine reads it in.
  869.  */
  870.  
  871. static int
  872. _pgjb_mdblockrd(reln, item, buf, blkno)
  873.     Relation reln;
  874.     SJCacheItem *item;
  875.     char *buf;
  876.     int blkno;
  877. {
  878.     int which;
  879.     int offset;
  880.  
  881.     which = blkno % SJGRPSIZE;
  882.     offset = (which * BLCKSZ) + JBBLOCKSZ;
  883.  
  884.     if (FileSeek(reln->rd_fd, 0L, L_SET) != 0L) {
  885.     elog(NOTICE, "_pgjb_mdblockrd: cannot seek");
  886.     return (SM_FAIL);
  887.     }
  888.  
  889.  
  890.     if (FileRead(reln->rd_fd, &(buf[offset]), BLCKSZ) <= 0) {
  891.     elog(NOTICE, "_pgjb_mdblockrd: can't get block off mag disk");
  892.     return (SM_FAIL);
  893.     }
  894.  
  895.     /* it's heeeere... */
  896.     item->sjc_flags[which] &= ~SJC_MISSING;
  897.  
  898.     return (SM_SUCCESS);
  899. }
  900.  
  901. /*
  902.  *  JBShmemSize() -- return amount of shared memory required for jukebox
  903.  *             connection state.
  904.  */
  905.  
  906. int
  907. JBShmemSize()
  908. {
  909.     int size;
  910.     int nsegs;
  911.     int nbuckets;
  912.     int tmp;
  913.  
  914.     /* size of hash table */
  915.     nbuckets = 1 << my_log2((JBCACHESIZE - 1) / DEF_FFACTOR + 1);
  916.     nsegs = 1 << my_log2((nbuckets - 1) / DEF_SEGSIZE + 1);
  917.     size = my_log2(JBCACHESIZE) + sizeof(HHDR);
  918.     size += nsegs * DEF_SEGSIZE * sizeof(SEGMENT);
  919.     tmp = (int)ceil((double)JBCACHESIZE/BUCKET_ALLOC_INCR);
  920.     size += tmp * BUCKET_ALLOC_INCR *
  921.             (sizeof(BUCKET_INDEX) + sizeof(JBHashEntry));
  922.  
  923.     /* size of integer telling us how full hash table is */
  924.     size += sizeof(*JBNEntries);
  925.  
  926.     return (size);
  927. }
  928.  
  929. #endif /* SONY_JUKEBOX */
  930.